import os
import sys
import time

import numpy as np
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
# from PyQt5.QtWebEngineWidgets import *

from ui.UI_MainWindow import MainWindow
from utils.func import *
from utils.state import State
from utils.image_sub_thread import ImageThread, SobelThread

MAIN_PATH = os.path.dirname(os.path.abspath(__file__))
CLEAR_ALL = 7
CLEAR_SRC_AND_DST = 6
CLEAR_SRC_AND_EXT = 5
CLEAR_SRC = 4
CLEAR_DST_AND_EXT = 3
CLEAR_DST = 2
CLEAR_EXT = 1

INIT_STATE = 0
ACT_OPEN = 1
ACT_OPEN_FOLDER = 2
ACT_CLOSE = 3
ACT_SAVE = 4
ACT_QUIT = 5
ACT_PREV = 6
ACT_NEXT = 7
ACT_PLOT_HIST = 8
ACT_BINARIZE = 9
ACT_EXP_TRANS = 10
ACT_LOG_TRANS = 11
ACT_HIST_EQL = 12
ACT_SOBEL = 13
ACT_ERODE = 14
ACT_DILATE = 15
ACT_OPENING = 16
ACT_CLOSING = 17
ACT_FILTER_MEAN = 18
ACT_FILTER_MEDIAN = 19
ACT_FILTER_GAUSSIAN = 20
ACT_CONC = 21
ACT_TRANSLATE = 22
ACT_ROTATE = 23
ACT_SCALE = 24
ACT_FLIP = 25


class Window:
    def __init__(self):
        self.ui = MainWindow()
        self.image = None  # 类的属性作为`全局变量`
        self.images = []
        self.index = -1
        self.state = State()
        self.load_settings()
        self.initConnecttion()

        # 重写成员变量的关闭事件
        self.ui.closeEvent = self.closeEvent

        self.initUi()

    def initConnecttion(self):
        # 绑定信号(Signal)与槽(Slot)，当菜单栏对应位置被点击时，会触发对应的槽函数来响应
        self.ui.act_open.triggered.connect(self.slot_open)
        self.ui.act_open_folder.triggered.connect(self.slot_open_folder)
        self.ui.act_close.triggered.connect(self.slot_close)
        self.ui.act_save.triggered.connect(self.slot_save)
        self.ui.act_quit.triggered.connect(self.slot_quit)
        self.ui.act_prev.triggered.connect(self.slot_prev)
        self.ui.act_next.triggered.connect(self.slot_next)

        self.ui.act_plot_hist.triggered.connect(self.slot_plot_hist)

        self.ui.act_binarize.triggered.connect(self.slot_binarize)
        self.ui.act_exp_trans.triggered.connect(self.slot_exp_trans)
        self.ui.act_log_trans.triggered.connect(self.slot_log_trans)
        self.ui.act_hist_eql.triggered.connect(self.slot_hist_eql)

        self.ui.act_sobel.triggered.connect(self.slot_sobel)

        self.ui.act_erode.triggered.connect(self.slot_erode)
        self.ui.act_dilate.triggered.connect(self.slot_dilate)
        self.ui.act_opening.triggered.connect(self.slot_opening)
        self.ui.act_closing.triggered.connect(self.slot_closing)

        self.ui.act_filter_mean.triggered.connect(self.slot_filter_mean)
        self.ui.act_filter_median.triggered.connect(self.slot_filter_median)
        self.ui.act_filter_gaussian.triggered.connect(self.slot_filter_gaussian)

        self.ui.act_conc.triggered.connect(self.slot_conc)

        self.ui.act_translate.triggered.connect(self.slot_translate)
        self.ui.act_flip.triggered.connect(self.slot_flip)
        self.ui.act_scale.triggered.connect(self.slot_scale)
        self.ui.act_rotate.triggered.connect(self.slot_rotate)

        self.ui.act_set_threshold.triggered.connect(self.slot_set_threshold)
        self.ui.act_set_gamma.triggered.connect(self.slot_set_gamma)
        self.ui.act_set_dilate_iter.triggered.connect(self.slot_set_dilate_iter)
        self.ui.act_set_erode_iter.triggered.connect(self.slot_set_erode_iter)
        self.ui.act_set_opening_iter.triggered.connect(self.slot_set_opening_iter)
        self.ui.act_set_closing_iter.triggered.connect(self.slot_set_closing_iter)

        self.ui.act_help.triggered.connect(self.slot_help)

    def initUi(self):
        x = self.state.get_x()
        y = self.state.get_y()
        width = self.state.get_width()
        height = self.state.get_height()
        self.ui.setGeometry(x, y, width, height)

    # -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- #
    # 以下都是槽函数，在initConnection中绑定了信号与槽函数之后，相应的动作会得到对应的槽函数的响应

    # 打开图片
    def slot_open(self):
        try:
            file_name, _ = QFileDialog.getOpenFileName(self.ui, "打开文件", self.state.get_last_path(), "*.bmp *.raw")
            # 文件名为空或者状态锁被占用时，直接返回
            if file_name is None or file_name == '' or self.state.get_lock():
                return
            self.state.set_current_action(ACT_OPEN)
            self.state.set_last_path(file_name)
            # 之所有要将这两者置空，是为了防止使用过【打开文件夹】后，再使用【打开文件】，图片列表依旧存在的情况
            self.index = -1
            self.images = []
            self.init_loading()
            self.thread = ImageThread(open_image, file_name)
            self.thread.sig.connect(self.callback_image_src_done)
            self.thread.start()

        except FileNotFoundError:
            QMessageBox.warning(self.ui, "错误", "文件不存在！")

    # 打开目录
    def slot_open_folder(self):
        try:
            dir_name = QFileDialog.getExistingDirectory(self.ui, "打开目录", self.state.get_last_dir())
            if dir_name is None or dir_name == '' or self.state.get_lock():
                return
            self.state.set_current_action(ACT_OPEN_FOLDER)
            self.state.set_last_dir(dir_name)
            file_names = os.listdir(dir_name)
            self.images = []
            for file_name in file_names:
                if file_name.endswith('.bmp') or file_name.endswith('.raw'):
                    self.images.append((os.path.join(dir_name, file_name)))
            self.index = 0
            # self.clear_figure()
            # self.image = open_image(self.images[self.index])
            # show_image(self.image, self.ui.label_src, 20, 60 + self.ui.base_offset)
            self.init_loading()
            self.thread = ImageThread(open_image, self.images[self.index])
            self.thread.sig.connect(self.callback_image_src_done)
            self.thread.start()
            self.ui.status_bar.showMessage(
            f'当前图片索引：{self.index + 1}/{len(self.images)}  当前图片路径：{self.images[self.index]}')

        except NotADirectoryError:
            QMessageBox.warning(self.ui, "错误", "文件夹不存在！")

    # 上一张
    def slot_prev(self):
        if self.index < 1:  # 未打开目录或者没有上一张
            QMessageBox.information(self.ui, "提示", "没有上一张了")
            return
        self.state.set_current_action(ACT_PREV)
        self.index -= 1  # 当前索引 -1
        # self.image = open_image(self.images[self.index])  # 打开当前图片
        # self.clear_figure()
        # show_image(self.image, self.ui.label_src, 20, 60 + self.ui.base_offset)  # 显示当前图片
        self.init_loading()
        self.thread = ImageThread(open_image, self.images[self.index])
        self.thread.sig.connect(self.callback_image_src_done)
        self.thread.start()
        # 状态栏显示当前图片的索引
        self.ui.status_bar.showMessage(
            f'当前图片索引：{self.index + 1}/{len(self.images)}  当前图片路径：{self.images[self.index]}')

    # 下一张
    def slot_next(self):
        if self.index >= len(self.images) - 1:  # 没有下一张
            QMessageBox.information(self.ui, "提示", "没有下一张了")
            return
        self.state.set_current_action(ACT_NEXT)
        self.index += 1  # 当前索引 +1
        # self.image = open_image(self.images[self.index])  # 打开当前图片
        # self.clear_figure()
        # show_image(self.image, self.ui.label_src, 20, 60 + self.ui.base_offset)  # 显示当前图片
        self.init_loading()
        self.thread = ImageThread(open_image, self.images[self.index])
        self.thread.sig.connect(self.callback_image_src_done)
        self.thread.start()
        self.ui.status_bar.showMessage(
            f'当前图片索引：{self.index + 1}/{len(self.images)}  当前图片路径：{self.images[self.index]}')

    # 关闭所有图片
    def slot_close(self):
        self.state.set_current_action(ACT_CLOSE)
        self.clear_figure()
        self.ui.label_ext.resize(0, 0)
        self.index = -1
        self.images = []
        self.image = None
        # 清空状态栏
        self.ui.status_bar.showMessage('')

    # 保存图片
    def slot_save(self):
        # TODO: 这一部分可以简化
        im = self.ui.label_dst.pixmap()
        if im is None:
            return
        qim = im.toImage()
        w = qim.width()
        h = qim.height()
        dst = np.zeros((h, w), np.uint8)
        for i in range(h):
            for j in range(w):
                pix = qRed(qim.pixel(j, i))
                dst[i][j] = pix
        file_name = QFileDialog.getSaveFileName(self.ui, '保存图片', './', '*.raw')[0]
        # dst.tofile(file_name)
        with open(file_name, 'wb') as f:
            head = struct.pack('ii', w, h)
            for i in range(h):
                for j in range(w):
                    if i == 0 and j == 0:
                        s = struct.pack('B', dst[i][j])
                    else:
                        s += struct.pack('B', dst[i][j])
            s_bytes = head + s
            f.write(s_bytes)
        self.state.set_current_action(ACT_SAVE)

    # 退出应用程序
    def slot_quit(self):
        self.state.set_current_action(ACT_QUIT)
        # 程序退出前保存设置
        self.save_settings()
        self.ui.close()

    # 绘制直方图
    def slot_plot_hist(self):
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            QMessageBox.information(self.ui, "提示", "请先打开图片")
            return
        self.state.set_current_action(ACT_PLOT_HIST)
        self.init_loading()
        self.thread = ImageThread(plot_hist, self.image)
        self.thread.sig.connect(self.callback_image_done)
        self.thread.start()

    # 图像二值化
    def slot_binarize(self):
        self.state.set_current_action(ACT_BINARIZE)
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        th = self.state.get_threshold()
        self.init_loading()
        self.thread = ImageThread(binarize, self.image, th)
        self.thread.sig.connect(self.callback_image_done)
        self.thread.start()
        # 显示阈值
        self.ui.status_bar.showMessage(f'阈值：{th}')

    # 幂次变换
    def slot_exp_trans(self):
        self.state.set_current_action(ACT_EXP_TRANS)
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        gamma = self.state.get_gamma()
        self.init_loading()
        self.thread = ImageThread(exp_trans, self.image, gamma)
        self.thread.sig.connect(self.callback_image_done)
        self.thread.start()

        # 显示gamma  
        self.ui.status_bar.showMessage(f'gamma={gamma}')

    # 对数变换
    def slot_log_trans(self):
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        self.state.set_current_action(ACT_LOG_TRANS)
        self.init_loading()
        self.thread = ImageThread(log_trans, self.image)
        self.thread.sig.connect(self.callback_image_done)
        self.thread.start()

    # 直方图均衡
    def slot_hist_eql(self):
        if self.image is None or self.image.ndim == 3  or self.state.get_lock():
            return
        self.state.set_current_action(ACT_HIST_EQL)
        self.init_loading()
        self.thread = ImageThread(hist_eql, self.image)
        self.thread.sig.connect(self.callback_image_done)
        self.thread.start()


    # Sobel算子边缘提取
    def slot_sobel(self):
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        self.state.set_current_action(ACT_SOBEL)
        self.init_loading()
        self.thread = SobelThread(sobel, self.image)
        self.thread.sig.connect(self.callback_image_done_sobel)
        self.thread.start()

    # 腐蚀
    def slot_erode(self):
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        self.state.set_current_action(ACT_ERODE)
        self.init_loading()
        erode_iter = self.state.get_erode_iter()
        self.thread = ImageThread(erode, self.image, erode_iter)
        self.thread.sig.connect(self.callback_image_done)
        self.thread.start()

    # 膨胀
    def slot_dilate(self):
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        self.state.set_current_action(ACT_DILATE)
        self.init_loading()
        dilate_iter = self.state.get_dilate_iter()
        self.thread = ImageThread(dilate, self.image, dilate_iter)
        self.thread.sig.connect(self.callback_image_done)
        self.thread.start()

    # 开运算
    def slot_opening(self):
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        self.state.set_current_action(ACT_OPENING)
        self.init_loading()
        opening_iter = self.state.get_opening_iter()
        self.thread = ImageThread(opening, self.image, opening_iter)
        self.thread.sig.connect(self.callback_image_done)
        self.thread.start()

    # 闭运算
    def slot_closing(self):
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        self.state.set_current_action(ACT_CLOSING)
        self.init_loading()
        closing_iter = self.state.get_closing_iter()
        self.thread = ImageThread(closing, self.image, closing_iter)
        self.thread.sig.connect(self.callback_image_done)
        self.thread.start()

    # 均值滤波
    def slot_filter_mean(self):
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        self.state.set_current_action(ACT_FILTER_MEAN)
        self.init_loading()
        self.thread = ImageThread(mean_filter, self.image)
        self.thread.sig.connect(self.callback_image_done)
        self.thread.start()

    # 中值滤波
    def slot_filter_median(self):
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        self.state.set_current_action(ACT_FILTER_MEDIAN)
        self.init_loading()
        self.thread = ImageThread(median_filter, self.image)
        self.thread.sig.connect(self.callback_image_done)
        self.thread.start()

    # 高斯滤波
    def slot_filter_gaussian(self):
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        self.state.set_current_action(ACT_FILTER_GAUSSIAN)
        self.init_loading()
        self.thread = ImageThread(gaussian_filter, self.image)
        self.thread.sig.connect(self.callback_image_done)
        self.thread.start()

    # 连通域分析
    def slot_conc(self):
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        self.state.set_current_action(ACT_CONC)
        self.init_loading()
        self.thread = ImageThread(conc, self.image)
        self.thread.sig.connect(self.callback_image_done)
        self.thread.start()

    # 平移
    def slot_translate(self):
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        self.state.set_current_action(ACT_TRANSLATE)
        self.init_loading()
        self.thread = ImageThread(translate, self.image, 150, 0)
        self.thread.sig.connect(self.callback_image_done)
        self.thread.start()

    # 翻转
    def slot_flip(self):
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        self.state.set_current_action(ACT_FLIP)
        h, w = self.image.shape[:2]
        # 水平翻转
        M_x = np.array([[-1, 0, w - 1],
                        [0, 1, 0]])
        # 垂直翻转
        M_y = np.array([[1, 0, 0],
                        [0, -1, h - 1]])
        # 既水平也垂直翻转（相当于旋转180°）
        M_xy = np.array([[-1, 0, w - 1],
                         [0, -1, h - 1]])
        M = M_xy
        # out_image = flip(self.image, M)
        # 加载动画
        self.init_loading()
        # 创建子线程
        self.thread = ImageThread(flip, self.image, M)
        # 连接信号
        self.thread.sig.connect(self.callback_image_done)
        # 开始线程
        self.thread.start()

    # 缩放
    def slot_scale(self):
        # TODO: 缩放的效率问题
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        self.state.set_current_action(ACT_SCALE)
        f_x = 2
        f_y = 2
        self.init_loading()
        # 创建子线程
        self.thread = ImageThread(scale, self.image, f_x, f_y)
        # 连接信号
        self.thread.sig.connect(self.callback_image_done)
        # 开始线程
        self.thread.start()

    # 旋转
    def slot_rotate(self):
        if self.image is None or self.image.ndim == 3 or self.state.get_lock():
            return
        self.state.set_current_action(ACT_ROTATE)
        theta = 45
        self.init_loading()
        # 创建子线程
        self.thread = ImageThread(rotate, self.image, theta)
        # 连接信号
        self.thread.sig.connect(self.callback_image_done)
        # 开始线程
        self.thread.start()

    # 设置gamma
    def slot_set_gamma(self):
        value = self.state.get_gamma()
        num, ok = QInputDialog.getDouble(self.ui, "设置gamma", "gamma:", value, 0.1, 10.0, 1)
        if ok:
            self.state.set_gamma(num)
            if self.state.get_current_action() == ACT_EXP_TRANS:
                self.slot_exp_trans()

    # 设置阈值
    def slot_set_threshold(self):
        value = self.state.get_threshold()
        # 弹出输入对话框
        num, ok = QInputDialog.getInt(self.ui, '设置阈值', '请输入阈值：', value, 0, 255)
        if ok:
            self.state.set_threshold(num)
            if self.state.get_current_action() == ACT_BINARIZE:
                self.slot_binarize()

    # 设置腐蚀迭代次数
    def slot_set_erode_iter(self):
        value = self.state.get_erode_iter()
        # 弹出输入对话框
        num, ok = QInputDialog.getInt(self.ui, '设置腐蚀迭代次数', '请输入腐蚀迭代次数：', value, 1, 10)
        if ok:
            self.state.set_erode_iter(num)

    # 设置膨胀迭代次数
    def slot_set_dilate_iter(self):
        value = self.state.get_dilate_iter()
        # 弹出输入对话框
        num, ok = QInputDialog.getInt(self.ui, '设置膨胀迭代次数', '请输入膨胀迭代次数：', value, 1, 10)
        if ok:
            self.state.set_dilate_iter(num)

    # 设置开运算迭代次数
    def slot_set_opening_iter(self):
        value = self.state.get_opening_iter()
        # 弹出输入对话框
        num, ok = QInputDialog.getInt(self.ui, '设置开运算迭代次数', '请输入开运算迭代次数：', value, 1, 10)
        if ok:
            self.state.set_open_iter(num)

    # 设置闭运算迭代次数
    def slot_set_closing_iter(self):
        value = self.state.get_closing_iter()
        # 弹出输入对话框
        num, ok = QInputDialog.getInt(self.ui, '设置闭运算迭代次数', '请输入闭运算迭代次数：', value, 1, 10)
        if ok:
            self.state.set_close_iter(num)

    # 打开帮助
    def slot_help(self):
        # import os
        # browser = QWebEngineView()
        # # path = 'README.html'
        # # url = os.path.join(os.getcwd(), path)
        # # browser.load(QUrl.fromLocalFile(url))
        # browser.setUrl('https://gitee.com/zhang_qi_hao/im-pro/blob/master/README.md')
        # self.ui.widget_help.setCentralWidget(browser)
        # self.ui.widget_help.show()
        # self.ui.widget_help.raise_()
        return

        # 清空图片，由于图片都需要一个QLabel去装载，为了方便装载后清理图片，这里预设了3个用于装载图片的QLabel，code决定了清楚哪几张图片

    def clear_figure(self, code=CLEAR_ALL):
        if code == CLEAR_ALL:
            self.ui.label_src.clear()  # 4
            self.ui.label_dst.clear()  # 2
            self.ui.label_ext.clear()  # 1
        elif code == CLEAR_SRC_AND_DST:
            self.ui.label_src.clear()  # 4
            self.ui.label_dst.clear()  # 2
        elif code == CLEAR_SRC_AND_EXT:
            self.ui.label_src.clear()  # 4
            self.ui.label_ext.clear()  # 1
        elif code == CLEAR_SRC:
            self.ui.label_src.clear()  # 4
        elif code == CLEAR_DST_AND_EXT:
            self.ui.label_dst.clear()  # 2
            self.ui.label_ext.clear()  # 1
        elif code == CLEAR_DST:
            self.ui.label_dst.clear()  # 2
        elif code == CLEAR_EXT:
            self.ui.label_ext.clear()  # 1
        else:
            return

    # 初始化加载动画
    def init_loading(self):
        # 清除画布
        self.clear_figure(CLEAR_DST_AND_EXT)
        # 将对应的label移动到最上层和当前窗口正中间
        self.ui.label_loading.move(self.ui.label_loading.parent().width() / 2 - self.ui.label_loading.width() / 2,
                                   self.ui.label_loading.parent().height() / 2 - self.ui.label_loading.height() / 2)
        self.ui.label_loading.raise_()
        self.ui.label_loading.show()
        # 开始播放
        self.ui.movie.start()
        # 一旦开启异步操作就将状态锁锁上
        self.state.lock()

    # 关闭动画
    def close_loading(self):
        self.ui.label_loading.hide()
        self.ui.movie.stop()
        self.state.unlock()

    # 专用于在label_src显示图片的回调
    def callback_image_src_done(self, image):
        self.image = image
        self.clear_figure(CLEAR_ALL)
        show_image(image, self.ui.label_src, 30, 60+self.ui.base_offset)
        self.close_loading()

    # 处理图片完成的回调
    def callback_image_done(self, out_image):
        _, w = self.image.shape[:2]  # 这里获取的是原图的尺寸,因为目标要在原图基础上便宜
        self.clear_figure(CLEAR_DST)
        show_image(out_image, self.ui.label_dst, w + 30, 60 + self.ui.base_offset)
        self.close_loading()

    # 对于Sobel算子边缘提取，由于返回图像时两个方向上的，所以分别处理
    def callback_image_done_sobel(self, out_image):
        out_image_x, out_image_y = out_image
        _, w = self.image.shape[:2]  # 这里获取的是原图的尺寸,因为目标要在原图基础上便宜
        self.clear_figure(CLEAR_DST_AND_EXT)
        show_image(out_image_x, self.ui.label_dst, w + 30, 60 + self.ui.base_offset)
        show_image(out_image_y, self.ui.label_ext, w * 2 + 60, 60 + self.ui.base_offset)
        self.close_loading()

    # 打开配置文件
    def load_settings(self):
        # 将配置文件中相应的字段加载到state中
        settings = QSettings(os.path.join(MAIN_PATH, "config/setting.ini"), QSettings.IniFormat)
        self.state.set_gamma(settings.value('gamma', type=float))
        self.state.set_last_path(settings.value('last_path', type=str))
        self.state.set_last_dir(settings.value('last_dir', type=str))
        self.state.set_threshold(settings.value('threshold', type=int))
        self.state.set_dilate_iter(settings.value('dilate_iter', type=int))
        self.state.set_erode_iter(settings.value('erode_iter', type=int))
        self.state.set_opening_iter(settings.value('opening_iter', type=int))
        self.state.set_closing_iter(settings.value('closing_iter', type=int))
        self.state.set_x(settings.value('x', type=int))
        self.state.set_y(settings.value('y', type=int))
        self.state.set_width(settings.value('width', type=int))
        self.state.set_height(settings.value('height', type=int))

    # 保存配置文件(并非所有state的属性都要保存)
    def save_settings(self):
        settings = QSettings(os.path.join(MAIN_PATH, "config/setting.ini"), QSettings.IniFormat)
        settings.setValue('gamma', self.state.get_gamma())
        settings.setValue('last_path', self.state.get_last_path())
        settings.setValue('last_dir', self.state.get_last_dir())
        settings.setValue('threshold', self.state.get_threshold())
        settings.setValue('dilate_iter', self.state.get_dilate_iter())
        settings.setValue('erode_iter', self.state.get_erode_iter())
        settings.setValue('opening_iter', self.state.get_opening_iter())
        settings.setValue('closing_iter', self.state.get_closing_iter())
        settings.setValue('x', self.state.get_x())
        settings.setValue('y', self.state.get_y())
        settings.setValue('width', self.state.get_width())
        settings.setValue('height', self.state.get_height())

    # 关闭窗口时保存配置文件
    def closeEvent(self, event):
        x = self.ui.geometry().x()
        y = self.ui.geometry().y()
        self.state.set_x(x)
        self.state.set_y(y)
        self.save_settings()
        event.accept()




if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.ui.show()
    sys.exit(app.exec_())
